perm filename TCPMSC.MAC[IP,SYS] blob sn#680223 filedate 1982-10-14 generic text, type T, neo UTF8
;CWL:<403-TCP>TCPMSC.MAC.40303 26-Apr-82 17:14:48, Edit by CLYNN
; Max segment option subbort, IP information support
;<403-TCP>TCPMSC.MAC.40301 29-Jan-82 15:04:02, Edit by CLYNN
; Updated for TCP release 3
; Combined pre-FILHDR code & FILHDR into TCPIPK
; MAXTCB becomes TCBMAX in STG, TRACEP becomes INTTRC
;[BBNF]<401-TCP>TCPMSC.MAC.128, 27-Jul-81 12:48:00, Ed: CLYNN
; Fix: Matching TCB check at CHKAD9, No memory return in TCBINI,
; No space for new TCB in NEWTCB
;<MNET-TCP>TCPMSC.MAC.3,  4-Apr-81 17:22:39, Edit by TAPPAN
; Add multinet stuff under a conditional

	SEARCH	INPAR,TCPPAR,PROLOG
IFN MNET,<SEARCH MNTPAR>
	TTITLE	TCPMSC
	SUBTTL	TCP Miscellaneous routines.  William W. Plummer, 14Jan77
	SWAPCD

COMMENT !

* CHKWND ...  3 ...... Check if a sequence number is within a window
* OVRLAP ...  4 ...... Check if two segments overlap

* ONLCLT ...  5 ...... Check if source of packet is using our clock
* GETISN ...  5 ...... Get current value of Initial Sequence Number

* PKTEND ...  6 ...... Returns sequence number of end of packet plus one
* NULPKT ...  6 ...... Test a packet for containing something to be ACKd
* TRMPKT ...  7 ...... Trim storage required by a packet
* CLRBLK ...  8 ...... Clear a block before stuffing it

* LCKCAL ...  9 ...... Lock a lock and call a funtion
* CHKADD ... 10 ...... Lookup a connection and maybe create new one
* TCBINI ... 14 ...... Initialize the TCB Hash Table
  NEWTCB ... 15 ...... Initialize a new connection block
* TCPMXP ... 17 ...... Update maximum packet size generated (TSMXP)
* TCPIPK ... 18 ...... Build packet & fill in common header information
	!

; CheckWindow(Left, Sequence, Right)

; Test "Sequence" to see if it is between "Left" (inclusive) and "Right"
; (not incl.).  Sequence numbers are  modulo MAXSEQ and are always
; positive when represented in a 36-bit word.

;T1/	Left
;T2/	Sequence
;T3/	Right
;
;	CALL CHKWND
;Ret+1:	always.  T1 has -1 if Sequence is in the window, 0 if not

CHKWND::TEMP <VAL,SEQ,RIGHT,LEFT>
	MOVEM T1,LEFT		; Make T1 available for value
	SETZ VAL,		; Init value
	CAMG LEFT,RIGHT		; Crosses 0?
	 SOSA VAL		; No. Get a -1 to return
	  EXCH LEFT,RIGHT	; Yes.  Reverse Left and Right.
	CAMGE SEQ,RIGHT
	 CAMGE SEQ,LEFT
	  SETCA VAL,		; Out of window.  Complement initial guess
	RESTORE
	RET

; Test to see if two sequence number segments have one or more common
; points.  The two segments are semi-open on the right, similar
; to CHKWND.

;T1/	Left1
;T2/	Right1
;T3/	Left2
;T4/	Right2
;
;	CALL OVRLAP
;Ret+1	always, T1 is -1 if overlap exists, 0 if not

OVRLAP::LOCAL <LEFT1,LEFT2,RIGHT2>
	MOVEM T1,LEFT1
	DMOVEM T3,LEFT2		; T3,T4 to LEFT2,RIGHT2
	EXCH T2,T3
	CALL CHKWND
	JUMPN T1,OVRLAX
	MOVE T1,LEFT2
	MOVE T2,LEFT1
	MOVE T3,RIGHT2
	CALL CHKWND
OVRLAX:	RESTORE
	RET

; Test to see if the current packet was sent by a host which
; is using the same timebase as this host.  So that we will know
; if the time stamp is valid


;PKT/	(Extended) Pointer to the packet under consideration
;
;	CALL ONLCLT
;Ret+1:	always, T1 non-0 if packet has a useable timestamp

ONLCLT::LOAD T1,PISH,(PKT)	; Get the 32-bit source address
IFN MNET,<			; If multiple address'
	CALL LCLHST		; Is it me?
	  SETZ T1,		; No, clear T1
>
IFE MNET,<
	CAME T1,INETID		; Is it me?
	 TDZA T1,T1		; No, clear it
	  SETO T1,		; Else -1
>
	RET



; Get the current value of the Initial Sequence Number curve.  This
; curve is a straight line which starts at 0 and goes through the
; maximum sequence number minus 1 every cycle time of the network.
; It steps once a second.

;	CALL GETISN
;Ret+1:	always, ISN in T1

GETISN::
IFKA <	GTAD			; Day,,Sec
	HRRZS T1		; Save second number within day
	DIVI T1,↑D<24*60*60>	; Get binary fraction of day
>
IFNKA <	CALL LGTAD		; Day,,tick
	HRRZS T1		; Save tick with day
	LSH T1,↑D17		; Make into binary fraction of day
>
; TCPISN is [↑D<<MAXSEQ/8>*<<24*60*60>/CYCTIM>>]
	MUL T1,TCPISN		; Get sequence number
	LSH T1,@TCPISN+1	; Scale factor of 8 above
	MODSEQ T1
	RET

; PKTEND(PKT) returns the sequence number following the packet

;PKT/	(Extended) Packet pointer
;TPKT/	(Extended) pointer to TCP part of packet
;
;	CALL PKTEND
;Ret+1:	always.  End of packet plus one in T1

PKTEND::LOAD T1,PSEQ,(TPKT)	; Get the start of the packet
	LOAD T2,PCTL,(TPKT)	; Get word containing control flags
	TXNE T2,<PSYN>		; Count one for SYN
	  ADDI T1,1
	TXNE T2,<PFIN>		; Another if FIN
	  ADDI T1,1
	LOAD T2,PIPL,(PKT)	; Length of whole packet in bytes
	LOAD T3,PIDO,(PKT)	; Number of words in Internet part
	LOAD T4,PTDO,(TPKT)	; Number of words in TCP header
	ADD T3,T4		; Total header word count
	ASH T3,2		; Byte count
	SUB T2,T3		; Difference is # bytes in data part
	ADD T1,T2		; Each data byte is one sequence slot
	MODSEQ T1		; Take MOD field size
	RET


; NULPKT(PKT)	Tells if packet doesn't contain anything ACK-able
;						(SYN, data, FIN)
;PKT/	(Extended) Packet pointer
;TPKT/	(Extended) pointer to TCP part of packet
;
;	CALL NULPKT
;Ret+1:	Always.  T1 -1 if packet is null, 0 if something

NULPKT::SETZ T1,		; Assume something ackable
	LOAD T2,PIPL,(PKT)	; Total packet length
	LOAD T3,PIDO,(PKT)	; Offset to Internet data in words
	LOAD T4,PTDO,(TPKT)	; Offset to TCP data in words
	ADD T3,T4		; Number of header words, total
	ASH T3,2		; Number of header bytes, total
	CAMLE T2,T3		; Anything in TCP data part?
	  JRST NULPKX		; Yes.  Packet is not null
	LOAD T2,PCTL,(TPKT)	; Get word of control flags
	TXNN T2,<PSYN!PFIN>
	  SETO T1,		; No data, no control.  Pkt is null.
NULPKX:	RET

; TRMPKT(PKT)		Return excess storage not used in packet block

;PKT/	(Extended) Packet pointer
;TPKT/	(Extended) pointer to TCP part of packet
;
;	CALL TRMPKT
;Ret+1:	Always.  PKT may come back 0 if storage unavailable.
; Copy the little piece into a new block.
; Return all of the whole (big) packet.

TRMPKT::JE PFSIZ,(PKT),TRMPKX	; Only full-size packets can be trimmed
	MOVE T1,INTNFI		; Number of free input buffer right now
	CAML T1,INTNIB		; Number desired
	  EXIT TRMPKX		; No reason to trim.  Space not tight.
	LOAD T1,PIPL,(PKT)	; Internet Packet Length in bytes
	ADDI T1,<4*PKTELI>+3	; Total length, set to round up
	ASH T1,-1		; Twice number of words in packet
	CAML T1,INTXPW		; Too big.  Wont leave a useful tail.
	  EXIT TRMPKX		; or lengths smashed on input packet? 
	ASH T1,-1		; # words in packet
	PUSH P,T1		; Save the new size

	MOVX T1,PT%TTP		; Going to trim packet
	TDNE T1,INTTRC		; Want trace?
	  CALL PRNPKT		; Yes

	MOVE T1,(P)		; New size, w
	CALL GETBLK		; And get a new little chunk
	POP P,T2		; Get back size
	JUMPE T1,[MOVX T1,PT%TKT ; Killed because no space
		TDNE T1,INTTRC	; Want trace?
		  CALL PRNPKT	; Yes
		JRST TRMPK9]	; Don't copy into non-X packet

	PUSH P,T1		; Save for later
	XMOVEI T3,PKTSII(T1)	; Destination
	MOVEI T1,-PKTSII(T2)	; Number of words to copy
	XMOVEI T2,PKTSII(PKT)	; Source
	CALL XBLTA		; Do a BLT
	POP P,T1		; Restore destination pkt ptr
	SETZM PKTQ(T1)		; Indicate not queued
	SETZRO PFLGS,(T1)	; Clear all internal flags
TRMPK9:
	PUSH P,T1		; Save pointer to the new packet (or 0)
	MOVE T2,TPKT		; Parallel pointer to TCP portion
	SUB T2,PKT		; Compute offset
	PUSH P,T2		; Save it
	CALL RETPKT		; Return be big piece
	POP P,TPKT		; Get back offset
	POP P,PKT		; Here's the replacement (or 0)
	ADD TPKT,PKT		; Reconstruct pointer
TRMPKX:	RET

; CLRBLK		Clear a block to be sure unstuffed fields are 0

;T1/	(Extended) pointer to block
;T2/	Size of block
;
;	CALL CLRBLK
;Ret+1:	Always.

CLRBLK::EXCH T1,T2		; Size to T1, Source to T2
	SUBI T1,1		; Number of transfers is 1 less
	XMOVEI T3,1(T2)		; Destination
	SETZM 0(T2)		; Clear a word.
	CALLRET XBLTA		; Clear the rest

; LCKCAL(Lock, Fn, Arg1, Arg2)

; Call Fn(Arg1, Arg2) with Lock set & NOINT

;T1/	(Extended) Lock pointer
;T2/	(Extended) Function address
;T3/	Arg1
;T4/	Arg2
;
;	CALL LCKCAL
;Ret+1:	always.  T1 has value of Fn(Arg1, Arg2)
; Can be used for cross-section calls

LCKCAL::PUSH P,T1		; (Ext) Lock address (save for UNLCK)
	PUSH P,T2		; (Ext) Function to CALL
	PUSH P,T3		; Save args from SETLCK
	PUSH P,T4

	MOVE T1,-3(P)		; Get pointer to the lock
	CALL SETLCK		; Test and set the lock (may wait)

	POP P,T2		; Put args in standard place for a call
	POP P,T1
	CALL @0(P)		; Call function
;LCKCA3:
	SUB P,BHC+1		; Drop Function address
	EXCH T1,0(P)		; Save the value while we unlock...
	CALL UNLCK
	POP P,T1
	RET

; CHKADD  Lookup up a connection and maybe create a new one

; If the desired (possibly wild) connection is found, the argument
;  function is called with the TCB locked.  If no TCB is found,
;  the value of CHKADD is -1,,errorcode.  Otherwise the value is
;  that of the function called, which may also be an error value.

; T1/	(Extended) Pointer to argument block defined by CHKADL
;			NOINT
;	CALL CHKADD
;Ret+1:	always.  Value in T1, TCB set up.  ELP+↑D13. Illegal port (LP=0)
;	(net)	ELP+↑D5. Pkt w/ wild FH/FP;  EFP+↑D7.  No TCB matches
;	(JSYS)	ELT+↑D4. No storage, wait bit, too many connections


CHKADD::STACKL <WILD1>
	CHKADL (<TCBX,TCB.FH,TCB.FP>) ; PARAMS & other LOCALs
	MOVEM T1,PARAMS		; Save pointer to parameters

	HRROI T1,ELP+↑D13	; "Illegal port"
	SKIPN LP
	  EXIT CHKADX		; Should never have to lookup wild LP
	HRROI T1,ELP+↑D5
	SKIPE FP		; Wild FP and/or FH ok only if "listen"
	 SKIPN FH
	  SKIPE JCN		; Called from a JSYS?
	   CAIA
	 EXIT CHKADX		; Bad packet from network (FP=0 or FH=0)

; Get unique access to the TCB Hash table.  This means having it locked
;  with the TCBH Use Count = 0.

CHKAD1:	XMOVEI T1,TCBHLK	; Pointer to the TCBH Lock
	CALL SETLCK		; Test and set the lock
	SKIPG TCBHUC		; TCBH Use Count.  Any readers?
	  JRST CHKAD3		; OK.  We have sole access.
	XMOVEI T1,TCBHLK	; Pointer to the lock
	CALL UNLCK		; Unlock it.
	MOVEI T1,TCBHUC		; Pointer to the use count
	CALL DISE		; Wait for it to go to zero
	JRST CHKAD1		; and try again.
CHKAD3:

; Get hash index to the TCB Hash table

	MOVE T1,LP		; Local port is what is hashed on
	LSH T1,-3
	ADDI T1,↑D23		; Hash LP into a TCBH index
	IMUL T1,LP
	IDIVI T1,TCBHSZ		; Size of the hash table
	ADD T2,TCBH		; (Ext) Location within TCBH
	MOVEM T2,TCBX		; Save the (ext) pointer to q head

; Scan the TCB queue which has its head in the slot at TCBX

	MOVE TCB,TCBX		; Initize the scan pointer
	SETZM WILD1		; No Wild match found yet
CHKAD4:	LOAD TCB,QNEXT,(TCB)	; Get next (first) thing on queue
	SETSEC TCB,INTSEC	; Make extended address
	CAMN TCB,TCBX		; Points back to head?
	  JRST CHKAD6		; Yes.  Scan done.
	LOAD T1,TOWNR,(TCB)	; Get Job number which owns this tcb
	SKIPE JCN		; Any job ok if called from net side
	 CAMN T1,JOBNO		; Must stay in this job
	  CAIA			; OK to think about this TCB
	   JRST CHKAD4		; Skip it and try next
	LOAD T1,TLP,(TCB)	; Get the Local Port from this TCB
	CAME T1,LP		; Does it match what we are looking for?
	  JRST CHKAD4		; No.  Try next TCB
	LOAD TCB.FH,TFH,(TCB)	; Get foreign host
	LOAD TCB.FP,TFP,(TCB)	; and foreign port
	CAMN TCB.FH,FH		; Compare these with what
	 CAME TCB.FP,FP		; is being sought
	  JRST CHKAD5		; Not an exact match.  Maybe OK for wild
	LOAD T4,TLH,(TCB)	; Check local address also
	CAME T4,LH
	  JFCL ; JRST CHKAD5	; Not an exact match.  Maybe OK for wild

; TCB points to an exact match.  If CHKADD was called from the JSYS
;  side, it means the user is trying to say more about the connection.

	SKIPE JCN		; Called from a JSYS?
	  JRST CHKAD6		; Yes.  Go use this exact match.
	LOAD T1,TRSYN,(TCB)	; Get state of Receive synchronization
	LOAD T2,TSSYN,(TCB)	; and state of Send synch.
	CAIN T1,NOTSYN		; Recv side still open?
	 CAIE T2,NOTSYN		; Send side still open?
	  JRST CHKAD6		; Yes.  Reuse this TCB.
	JRST CHKAD4		; Both closed. Pkt cannot reactivate
				; conn.  Look for another incarnation.

; See if this TCB should be remembered for use as a wild one.

CHKAD5:	SKIPN WILD1		; Continue scan if already have a wild match
	 SKIPN WILDOK		; Caller says OK to use wild TCB?
	  JRST CHKAD4		; No.  Keep looking for exact match.
	JUMPE TCB.FH,.+3 ;***	; TCB has wild foreign host
	CAME TCB.FH,FH	;***	; or exact match means ok.
	  JRST CHKAD4	;***	; No. Resume scan.
	JUMPE TCB.FP,.+3 ;***	; Wild foreign port in TCB?
	CAME TCB.FP,FP	;***	; or exact match?
	  JRST CHKAD4	;***	; No good.
	MOVEM TCB,WILD1	;***	; Save the location of the wild TCB
	JRST CHKAD4		; Continue looking for exact match.

; End of scan.  TCB has the TCB to use or points at queue head (TCBX)
; if none found.  WILD1 is 0 or pointer to a wild TCB.

CHKAD6:	CAME TCB,TCBX		; Found an exact match?
	  JRST CHKAD9		; Yes.  Go use TCB
	SKIPN WILD1		; Have a wild match?
	  JRST CHKAD7		; No.

; Bind a wild match.

	MOVE T1,FH		; Get the desired foreign host
	MOVE T2,FP		; and foreign port
	MOVE T3,LH		; and local host
	MOVE TCB,WILD1		; This is the TCB to work with
	STOR T1,TFH,(TCB)	; Store in the wild TCB
	STOR T2,TFP,(TCB)
	STOR T3,TLH,(TCB)
	JRST CHKAD9		; Do it.

; No wild match and no exact match.  If called from JSYS Create the connection.
; NOTE TCB=TCBX & WILD1=0

CHKAD7:	CAMN TCB,TCBX		; If have a TCB go to CHKAD9
	 SKIPN JCN		; Called from a JSYS?
	  JRST CHKAD9		; Net.  Don't create a TCB
; JSYS & no TCB
	MOVE T1,PARAMS		; Argument block address
	MOVE T2,TCBX		; (Ext) Where to enqueue the new TCB
	CALL NEWTCB		; Create it and initialize it.
	JUMPN TCB,CHKAD9	; Go use the new TCB
	MOVEI T1,TCBHLK		; Ran out of free storage.
	CALL UNLCK		; Unlock TCBH
	HRROI T1,ELT+↑D4	; "No room for another connection"
	EXIT CHKADX		; Report the error to the caller

; TCB has the TCB to use or is equal to TCBX if not.

CHKAD9:	JRST CHKA10	; Following doesn't work, why??
	CAME TCB,TCBX		; Found a TCB?
	 SKIPN JCN		; And called from the JSYS side?
	  JRST CHKA10		; No.
	LOAD T1,TJCN,(TCB)	; Get original JCN of TCB
	JUMPE T1,CHKA10		; None.  Use the new one.
	EXCH T1,JCN		; Use the original JCN
	CAME T1,JCN		; If temporary one is different,
	  CALL RETJCN		; Return it (RH of JCNTCB is 0)

; If a TCB was found/created, lock it and call the argument function.

CHKA10:	AOS TCBHUC		; Indicate TCBH has a reader
	MOVEI T1,TCBHLK		; Pointer to the TCBH lock
	CALL UNLCK		; Unlock TCBH with use count gt 0
	HRROI T1,EFP+↑D7	; "No such TCB"
	CAMN TCB,TCBX		; Did we locate a TCB?
	  JRST CHKA11		; No. Report the error
	XMOVEI T1,TCBLCK(TCB)	; Pointer to the TCB Lock
	MOVE T2,FN		; Function to be called
	MOVE T3,JCN		; Argument for the function
	MOVE T4,ARG1		; The argument passed through
	CALL LCKCAL		; Lock the lock and call the function
CHKA11:	SOS TCBHUC		; Indicate TCBH may change now

CHKADX:	CHKADR			; Restore
	RET

; TCBINI		Initialize the TCB Hash Table

;	CALL TCBINI
;Ret+1:	Always

TCBINI::LOCAL <TCBX>
	MOVEI T1,TCBHSZ*QSZ	; Size of the TCB Hash table
	CALL GETBLK		; Qs must point to things in same section!
	  JUMPE T1,TCBINX	; No space
	MOVEM T1,TCBH		; (Ext) Loc of hash table.
IF1 <IFN QSZ-1,<PRINTX ? QSZ isn't 1, Fix code>>
	MOVSI TCBX,-TCBHSZ	; Set to scan TCBH (assumes QSZ==1)
TCBIN1:	HRRZ T1,TCBX		; Index within TCBH table
	ADD T1,TCBH		; Pointer to table base
	CALL INITQ		; Initialize as a queue
	AOBJN TCBX,TCBIN1	; Loop over all slots
	SETZM TCBHUC		; Clear the use count
	XMOVEI T1,TCBHLK	; Pointer to the lock on TCBH
	CALL CLRLCK		; Initialize it
TCBINX:	RESTORE
	RET
; NEWTCB(LP, FH, FP, TCBX)	Initialize a new connection block

;T1/	PARAMS	; (Ext) address of parameter block
;T2/	TCBX	; (Ext) Adr of TCB Hash table entry
;
;	CALL NEWTCB
;Ret+1:	always.  TCB points to the TCB or is 0 if no space,
;		too many conn, no wait bits

NEWTCB:	CHKADL (TCBX)
	MOVEM T1,PARAMS		; Argument block address
	MOVEM T2,TCBX		; Where to enqueue new TCB

	MOVE T2,TCBCNT		; Current number of connections
	CAML T2,TCBMAX		; Test against max we support at once
	  JRST NEWTCE		; No room for another.
	MOVX T1,TCBSIZ		; Size of a connection block
	CALL GETBLK		; Get a block of free storage
	JUMPE T1,NEWTCE		; None available.  Fail.
	MOVEM T1,TCB		; Put (ext) adr in standard place
	MOVX T2,TCBSIZ		; Size again for CLRBLK
	CALL CLRBLK

	MOVE T1,LH		; Set the local host
	STOR T1,TLH,(TCB)
	STOR T1,TOPLH,(TCB)
	MOVE T2,LP		; Set the local port
	STOR T2,TLP,(TCB)
	MOVE T3,FH		; Set the foreign host
	STOR T3,TFH,(TCB)
	STOR T3,TOPFH,(TCB)
	MOVE T4,FP		; Set the foreign port
	STOR T4,TFP,(TCB)
	STOR T4,TOPFP,(TCB)

	JUMPN T4,NEWTC0		; Wild foreign port?
	SETONE TWLDP,(TCB)	; Yes.
NEWTC0:

	EXCH 0,T3
	LOAD T4,NETCLS		; Get network class bit(s)
	EXCH 0,T3
	TDZ T3,INTCLS(T4)	; Drop class bit(s)
	TDZE T3,INTNET(T4)	; Net field zero?
	  JRST NEWTC1		; No, but host might be.
	JUMPN T3,NEWTC2		; Yes, but host isn't, neither wild
	SETONE <TWLDN,TWLDT>,(TCB) ; Wild net & host
	JRST NEWTC2

NEWTC1:	JUMPN T3,NEWTC2		; Wild foreign host?
	SETONE TWLDT,(TCB)	; Yes. Wild host, net specified
NEWTC2:

	XMOVEI T1,TCBSBQ(TCB)	; TCB Send buffer queue
	CALL INITQ		; Initialize it.
	XMOVEI T1,TCBRXQ(TCB)	; TCB Retransmission queue
	CALL INITQ		; Initialize it.
	XMOVEI T1,TCBRBQ(TCB)	; TCB Receive buffer queue
	CALL INITQ		; Initialize it.
	XMOVEI T1,TCBRPQ(TCB)	; TCB Receive packet queue
	CALL INITQ		; Initialize it.

	CALL ASNWTB		; Assign a wait bit index for open/close
	JUMPL T1,NEWTC9		; Jump if we didn't get the bit.
	STOR T1,TOPNF,(TCB)	; Set into TCB
	CALL CLRWTB		; Initialize to zero state (closed)
	CALL ASNWTB		; Get another bit for error events
	JUMPL T1,NEWTC8		; Jump if that failed
	STOR T1,TERRF,(TCB)	; Set into TCB
	CALL CLRWTB		; Clear it.  (No error yet)
	XMOVEI T1,TCBLCK(TCB)	; Pointer to the TCB lock
	CALL CLRLCK		; Clear it.
	MOVX T1,↑D60		; Default time to live
	STOR T1,TTTL,(TCB)	; for this connection
	MOVX T1,<<1B<35-WID(PIPL)>>-1>	; Max possible packet 2**16-1 octets
	STOR T1,TSMXP,(TCB)	; including headers
	XMOVEI T1,TCBQ(TCB)	; (Ext) Pointer to the TCB just initialized
	MOVE T2,TCBX		; (Ext) Adr of TCB Hash table entry (of Q's)
	CALL NQ			; Place it on the right queue
	AOS TCBCNT		; Count as another connection
	EXIT NEWTCX

NEWTC8:	LOAD T1,TOPNF,(TCB)	; Oh well.  Have to back out.
	CALL RELWTB		; Release the open/close wait bit
NEWTC9:	MOVE T1,TCB		; Pointer to the connection block
	CALL RETBLK		; Give back that storage
NEWTCE:	SETZ TCB,		; Tell caller the bad news.
NEWTCX:	CHKADR
	RET
; TSMXP = TCPMXP (TCB)	Update maximum packet size generated on connection

; T1/	Foreign specified limit (from option), or 0
;	CALL TCPMXP

TCPMXP::
IFN MNET,<SAVP1>
	LOCAL <PSZ>
	SKIPLE PSZ,T1		; Save argument if specified
	  JRST TCPMX6		; Try to use it

	LOAD T1,TFH,(TCB)	; Get foreign address
IFN MNET,<CALL LCLHST>		; Is it one of us?
IFE MNET,<CAME T1,INETID>	; Is it to me?
	 SKIPA PSZ,[↑D576]	; No, sociable maximum
	  MOVE PSZ,TCPBYS	; Yes, big ones

; Packet Radio Kludge - small packets to certain nets

	LOAD T2,TFH,(TCB)	; Foreign address contains
	NETNUM T2,T2		; Foreign net number
	CAIE T2,↑D1		; BBN-PR
	 CAIN T2,↑D2		; SF-PR-1
	  MOVEI PSZ,↑D254
	CAIE T2,↑D5		; SILL-PR
	 CAIN T2,↑D6		; SF-PR-2
	  MOVEI PSZ,↑D254
	CAIE T2,↑D9		; BRAGG-PR
	 CAIN T2,↑D47		; SAC-PR
	  MOVEI PSZ,↑D254
; End of Kludge

; This isn't true if bypassing
	LOAD T1,TLH,(TCB)	; Foreigner's name for us
IFN MNET,<CALL FNDNCT		; Put address of interface in T2
	   CAIA			; Not found
	    SKIPG T1,NTPSIZ(P1)> ; Get max size for that (interface) net
IFE MNET,<NETNUM T2,T1		; Our net number ;CWL
	  SKIPG T1,INTSIZ(T2)>	; End of IFE MNET
	     MOVE T1,INTXPB	; None or 0, use maximum packet length
	CAMLE PSZ,T1		; Use it if smaller
	  MOVE PSZ,T1
TCPMX6:
	CAMLE PSZ,TCPBYS	; User want bigger than we support?
	  MOVE PSZ,TCPBYS	; Yes, Clamp to our max
	CAIGE PSZ,<.RTJST(-1,PIDO)+.RTJST(-1,PTDO)>*4+↑D8 ; Beware too
	  MOVX PSZ,<<.RTJST(-1,PIDO)+.RTJST(-1,PTDO)>*4+↑D8> ; small
	STOR PSZ,TSMXP,(TCB)	; Set max packet size for connection
	RESTORE
	RET
; TCPIPK(Min size (w), Max siz (w) or 0, Adr of addresses or 0 if TCB)

; Get space for packet and fill in headers

; T1/	0 (or maximum) data length, in octets
; T2/	Address of address block, or 0 if addresses in TCB are valid
;	Address block:	32-bit Destination address
;			32-bit Source (local) address
;			16-bit Destination port
;			16-bit Source port
; TCB/	Contains addresses if T2 is zero; may be zero

;	Call TCPIPK
;Ret+1:	  Cannot get space for packet, or neither T2 nor TCB specified
;Ret+2:	Success, PKT & TPKT set, packet headers & options set
;	PICKS/	contains maximum PIPL allowed
;	PIPL/	header+option length
;	T1/	PICKS-PIPL is available	for data

TCPIPK::LOCAL <SIZW,ADRS>
	MOVEM T2,ADRS		; Save Adr block address or 0

	ADD T2,TCB		; Make sure have TCB or Adr argument
	SKIPN T2		; Both T2 & TCB zero is error
	  JRST TCPIPV		; Lose

; Try to assign a block of free storage for the packet to be sent.

	MOVEI T2,TCPNPW		; Minimal packet (w/ max headers)
	EXCH T2,T1
	JUMPN T2,TCPIPB		; Use GETBBK for data

	MOVEM T1,SIZW		; Save minimal size
	CALL GETBLK		; Get block that big
	JUMPE T1,TCPIPV		; Lose
	JRST TCPIPC

TCPIPB:	ADDI T2,3		; Round up data count
	ASH T2,-2		; words
	ADD T2,T1
	CAMLE T2,INTXPW		; Don't ask for more than
	  MOVE T2,INTXPW	; Maximal pkt
	CALL GETBBK		; Get biggest block of free storage
	HLRZ SIZW,T1		; Size, wds, of block gotten, if any
	HRRZS T1		; Clear garbage from addr pointer		
	JUMPE T1,TCPIPV		; Lose
	SETSEC T1,INTSEC	; Make extended address (GETBBK)
TCPIPC:	MOVE PKT,T1		; Put in standard place

	MOVEI T1,PKTELI+<<MINIHS+3>/4>-1 ; Clear through IP header
	XMOVEI T2,(PKT)		; Source
	XMOVEI T3,1(T2)		; Destination
	SETZM 0(T2)		; Clear a word.
	CALL XBLTA		; Clear the rest

; Fill in IP header

	MOVEI T1,.INTVR
	STOR T1,PIVER,(PKT)	; Store protocol version number
	MOVEI T1,<MINIHS+3>/4	; # words in smallest IN hdr
	STOR T1,PIDO,(PKT)	; Set as initial data offset
	ASH T1,2		; Length, bytes
	STOR T1,PIPL,(PKT)	; Current length
	AOS T2,TCPSID		; Get the next segment ID
	STOR T2,PISID,(PKT)	; Into packet
	SETO T3,		; Max time to live
	JUMPE TCB,TCPIPE	; No TCB values, use 0
	LOAD T3,TTOS,(TCB)	; Copy Type of Service
	STOR T3,PITOS,(PKT)
	LOAD T3,TIFDF,(TCB)	; Copy Don't Fragment flag
	STOR T3,PIDF,(PKT)
	LOAD T3,TTTL,(TCB)	; Copy Time to Live
TCPIPE:	STOR T3,PITTL,(PKT)	; Set Time to Live
	MOVEI T3,.TCPFM		; TCP format
	STOR T3,PIPRO,(PKT)	; Set into protocol field

; Stuff in all pertinent Internet options so we can know where
; the TCP portion will begin

	CALL TCPIIO		; Insert IP Options & adjust PIDO
				;  and PIPL (Note TCB may be 0)

; Set pointer to TCP portion of packet now that all internet
; options have been set or reserved.

	XMOVEI TPKT,PKTELI(PKT)	; Pointer to Internet portion
	LOAD T2,PIDO,(PKT)	; Internet data offset (inc opt)
	ADDB T2,TPKT		; T2 & TPKT now point at TCP area of pkt
	MOVEI T1,<<MINTHS+3>/4>-1 ; Clear it
	XMOVEI T3,1(T2)		; Destination
	SETZM 0(T2)		; Clear a word.
	CALL XBLTA		; Clear the rest

; Fill in IP addresses & TCP header

	SKIPN ADRS		; Address block specified?
	  JRST TCPIPH		; No, use TCB

	DMOVE T1,(ADRS)		; Get addresses from arg block
	DMOVE T3,2(ADRS)
	JRST TCPIPI

TCPIPH:	LOAD T1,TFH,(TCB)	; Destination address
	LOAD T2,TLH,(TCB)	; Source address
	LOAD T3,TFP,(TCB)	; Destination port
	LOAD T4,TLP,(TCB)	; Source port
TCPIPI:
	STOR T1,PIDH,(PKT)	; Store the destination host
	STOR T2,PISH,(PKT)	; Store the source host
	STOR T3,PDP,(TPKT)	; Store the destination port
	STOR T4,PSP,(TPKT)	; Store the source port

	MOVEI T1,<<MINTHS+3>/4>	; Minimum TCP header size
	STOR T1,PTDO,(TPKT)	; Set into TCP data offset field
	ASH T1,2		; Bytes in TCP header
	LOAD T2,PIPL,(PKT)	; Current IP header length (inc opt)
	ADD T2,T1		; Total IP+TCP header length
	STOR T2,PIPL,(PKT)	; Set Internet packet length

; Now the Internet header is initialized and we can add TCP options.

	CALL TCPITO		; Insert TCP options & update PTDO
				; and PIPL (Note TCB may be 0)

IFL <WID(PICKS)-WID(PIPL)>,<? PRINTX Width of PIPL exceeds that of PICKS>
	MOVEI T1,-PKTELI(SIZW)	; Words for IP+TCP+DATA
	ASH T1,2		; Max # bytes (max PIPL)
	MOVE T2,INTXPB		; Maximum for all interfaces
	CAMLE T1,T2		; Bigger than max allowed?
	  MOVE T1,T2		; Yes, limit it
	SKIPE TCB		; Have TCB?
	  LOAD T2,TSMXP,(TCB)	; Connection maximum
	CAMLE T1,T2		; Bigger than connection max?
	  MOVE T1,T2		; Yes, limit it
	STOR T1,PICKS,(PKT)	; Save max PIPL (PICKS unused til end)
	LOAD T2,PIPL,(PKT)	; Total header size
	SUB T1,T2		; Octets available for data

	MOVE T2,TODCLK		; Current millisecond number
	STOR T2,PTS,(PKT)	; Store as timestamp

	TDZA T2,T2		; 0 OK
TCPIPV:	  SETO T2,		; NZ is bad
	RESTORE
	SKIPN T2		; Skip if bad
	  AOS (P)		; Skip if good
	RET

	T